package Hunters;

import AGENTS.AGENTS_ART;
import Bots.Bots;
import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
import cz.cuni.amis.pogamut.ut2004.agent.module.utils.TabooSet;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.NavigationState;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004PathAutoFixer;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotModuleController;
import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.UT2004ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Initialize;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Rotate;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StopShooting;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.*;
import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
import cz.cuni.amis.utils.collections.MyCollections;
import cz.cuni.amis.utils.exception.PogamutException;
import cz.cuni.amis.utils.flag.FlagListener;

import java.util.ArrayList;

@AgentScoped
public class HunterBot_BASELINE extends UT2004BotModuleController<UT2004Bot> {

    public int agt;

    int numSpace = 3;
    int CURSTATE = 0;
    int ACTION = 1;
    int REWARD = 2;


    static int numState = 28;
    static int numAction = 4;
    static int numReward = 2;

    static boolean immediateReward = false;

    public static Bots[] bots;
    public static AGENTS_ART[] agent;


    private final static int RFALCON = 0;
    private final static int TDFALCON = 1;
    private final static int BPN = 2;

    private final static int QLEARNING = 0;
    private final static int SARSA = 1;


    private UT2004PathAutoFixer autoFixer;

    private static int instanceCount = 0;


    //被打
    public IWorldEventListener<BotDamaged> botDamagedListener = new IWorldEventListener<BotDamaged>() {
        @Override
        public void notify(BotDamaged event) {
            if (bots[agt].r == 0) bots[agt].r = -0.1;
        }
    };


    //打人，+0.75
    public IWorldEventListener<PlayerDamaged> PlayerDamagedListener = new IWorldEventListener<PlayerDamaged>() {
        @Override
        public void notify(PlayerDamaged event) {
            bots[agt].hitdamage += (double) event.getDamage() / (double) 100;
            if (bots[agt].hitdamage > 1.0) bots[agt].hitdamage = 1.0;
            if (bots[agt].r < 0.75) bots[agt].r = 0.75;
        }
    };
    //被杀
    public IWorldEventListener<BotKilled> BotKilledListener = new IWorldEventListener<BotKilled>() {
        @Override
        public void notify(BotKilled event) {
            bots[agt].end = true;
            bots[agt].r = -1.0;
            bots[agt].reset();
        }
    };
//
    //杀敌


    public IWorldEventListener<PlayerKilled> PlayerKilledListener = new IWorldEventListener<PlayerKilled>() {
        @Override
        public void notify(PlayerKilled event) {
            if (bots[agt].r < 1) bots[agt].r = 1.0;
            bots[agt].enemy = null;
        }
    };

    //状态采样
    public void observe(Bots bots) {


        double lastheal = bots.stat[0];
        double lasthitdamage = bots.stat[14];


        bots.observe();

        //血量状态
        if (info.getHealth() / game.getMaxHealth() > lastheal) bots.r = 0.25;

        bots.stat[0] = info.getHealth() / game.getMaxHealth();
        if (bots.stat[0] > 1) bots.stat[0] = 1.0;
        bots.stat[1] = 1 - bots.stat[0];


        //看到敌人状态
        if (players.canSeeEnemies()) {
            bots.stat[6] = 0;
            bots.stat[7] = 1;
        }


        if (bots.shooting) {
            bots.stat[8] = 0;
            bots.stat[9] = 1;
        }

        if (!players.canSeeEnemies() && (info.isShooting() || info.isSecondaryShooting())) {
            getAct().act(new StopShooting());
        }

        if (weaponry.hasLoadedWeapon()) {
            bots.stat[10] = 0;
            bots.stat[11] = 1;
        }

        if (senses.isBeingDamaged()) {
            bots.stat[12] = 0;
            bots.stat[13] = 1;
        }
    	/*
    	if(bots.runningToPlayer) {
    		bots.stat[14]=0;
    		bots.stat[15]=1;
    	}
    	*/
        bots.stat[14] = bots.hitdamage;
        bots.stat[15] = 1 - bots.stat[14];

        if (bots.stat[14] > lasthitdamage) bots.r = 0.3;

        //动作状态
        for (int i = 0; i < numAction; i++) {
            if (bots.action == i) {
                bots.stat[16 + i] = 1;
                bots.stat[16 + numAction + i] = 0;
            }
        }
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //动作
    //////////////////
    // STATE ENGAGE //
    //////////////////
    protected void stopshooting(Bots bots) {
        if (!players.canSeeEnemies() && (info.isShooting() || info.isSecondaryShooting())) {
            getAct().act(new StopShooting());
        }
    }

    /**
     * Fired when bot see any enemy. <ol> <li> if enemy that was attacked last
     * time is not visible than choose new enemy <li> if enemy is reachable and the bot is far - run to him
     * <li> otherwise - stand still (kind a silly, right? :-)
     * </ol>
     */
    protected void stateEngage(Bots bots) {
        //log.info("Decision is: ENGAGE");
        //config.setName("Hunter [ENGAGE]");
        bot.getBotName().setInfo("stateEngage");

        double distance = Double.MAX_VALUE;

        // 1) pick new enemy if the old one has been lost
        if (bots.enemy == null || !bots.enemy.isVisible()) {
            // pick new enemy
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
            if (bots.enemy == null) {
                //log.info("Can't see any enemies... ???");
                return;
            }
        }

        // 2) stop shooting if enemy is not visible
        if (!bots.enemy.isVisible()) {
            if (info.isShooting() || info.isSecondaryShooting()) {
                // stop shooting
                getAct().act(new StopShooting());
            }
            bots.runningToPlayer = false;
        } else {
            // 2) or shoot on enemy if it is visible
            distance = info.getLocation().getDistance(bots.enemy.getLocation());
            if (shoot.shoot(weaponPrefs, bots.enemy) != null) {
                //log.info("Shooting at enemy!!!");
                bots.shooting = true;
            }
        }

        // 3) if enemy is far or not visible - run to him
        int decentDistance = Math.round(random.nextFloat() * 800) + 200;
        if (!bots.enemy.isVisible() || !bots.shooting || decentDistance < distance) {
            if (!bots.runningToPlayer) {
                navigation.navigate(bots.enemy);
                bots.runningToPlayer = true;
            }
        } else {
            bots.runningToPlayer = false;
            navigation.stopNavigation();
        }

        bots.item = null;
    }

    ///////////////
    // STATE HIT //
    ///////////////
    protected void stateHit(Bots bots) {
        //log.info("Decision is: HIT");
        bot.getBotName().setInfo("Rotate");
        if (navigation.isNavigating()) {
            navigation.stopNavigation();
            bots.item = null;
        }
        getAct().act(new Rotate().setAmount(32000));
    }

    //////////////////
    // STATE PURSUE //
    //////////////////

    /**
     * State pursue is for pursuing enemy who was for example lost behind a
     * corner. How it works?: <ol> <li> initialize properties <li> obtain path
     * to the enemy <li> follow the path - if it reaches the end - set lastEnemy
     * to null - bot would have seen him before or lost him once for all </ol>
     */
    protected void statePursue(Bots bots) {
        //log.info("Decision is: PURSUE");
        if (bots.enemy != null) {
            //bot.getBotName().setInfo("PURSUE");
            navigation.navigate(bots.enemy);
            bots.item = null;
        } else {
            //reset();
        }
    }

    protected int pursueCount = 0;

    //////////////////
    // STATE MEDKIT //
    //////////////////

    protected void stateMedKit(Bots bots) {
        //log.info("Decision is: MEDKIT");
        bot.getBotName().setInfo("MEDKIT");
        Item item = items.getPathNearestSpawnedItem(ItemType.Category.HEALTH);
        if (item == null) {
            //log.warning("NO HEALTH ITEM TO RUN TO => ITEMS");
            stateRunAroundItems(bots);
        } else {

            navigation.navigate(item);
            bots.item = item;
        }
    }

    ////////////////////////////
    // STATE RUN AROUND ITEMS //
    ////////////////////////////

    protected void stateRunAroundItems(Bots bots) {
        //log.info("Decision is: ITEMS");
        //config.setName("Hunter [ITEMS]");
        bot.getBotName().setInfo("stateRunAroundItems");
        if (navigation.isNavigatingToItem()) return;

        bots.interesting = new ArrayList<Item>();

        // ADD WEAPONS
        for (ItemType itemType : ItemType.Category.WEAPON.getTypes()) {
            if (!weaponry.hasLoadedWeapon(itemType)) bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        }
        // ADD ARMORS
        for (ItemType itemType : ItemType.Category.ARMOR.getTypes()) {
            bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        }
        // ADD QUADS
        bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.U_DAMAGE_PACK).values());
        // ADD HEALTHS
        if (info.getHealth() < 100) {
            bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.HEALTH_PACK).values());
        }

        Item item = MyCollections.getRandom(tabooItems.filter(bots.interesting));
        if (item == null) {
            //log.warning("NO ITEM TO RUN FOR!");
            if (navigation.isNavigating()) return;
            //bot.getBotName().setInfo("RANDOM NAV");
            navigation.navigate(navPoints.getRandomNavPoint());
        } else {
            bots.item = item;
            //log.info("RUNNING FOR: " + item.getType().getName());
            //bot.getBotName().setInfo("ITEM: " + item.getType().getName() + "");
            navigation.navigate(item);
        }
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public int healthLevel = 75;

    //ai部分
    private void doStep(AGENTS_ART agent, Bots bots) {

        observe(bots);

        // 1) do you see enemy? 	-> go to PURSUE (start shooting / hunt the enemy)
        if (players.canSeeEnemies() && weaponry.hasLoadedWeapon()) {
            stateEngage(bots);
            return;
        }

        // 2) are you shooting? 	-> stop shooting, you've lost your target
        if (info.isShooting() || info.isSecondaryShooting()) {
            getAct().act(new StopShooting());
        }

        // 3) are you being shot? 	-> go to HIT (turn around - try to find your enemy)
        if (senses.isBeingDamaged()) {
            this.stateHit(bots);
            return;
        }

        // 4) have you got enemy to pursue? -> go to the last position of enemy
        if (bots.enemy != null && weaponry.hasLoadedWeapon()) {  // !enemy.isVisible() because of 2)
            this.statePursue(bots);
            return;
        }

        // 5) are you hurt?			-> get yourself some medKit
        if (info.getHealth() < healthLevel) {
            this.stateMedKit(bots);
            return;
        }

        // 6) if nothing ... run around items
        stateRunAroundItems(bots);
    }

    //逻辑
    @Override
    public void logic() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        doStep(agent[agt], bots[agt]);
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public TabooSet<Item> tabooItems;
    //跟踪
    FlagListener<NavigationState> FlagListener = new FlagListener<NavigationState>() {
        @Override
        public void flagChanged(NavigationState changedValue) {
            agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
            switch (changedValue) {
                case PATH_COMPUTATION_FAILED:
                    //System.out.println("PATH_COMPUTATION_FAILED");
                case STUCK:
                    //System.out.println("STUCK");
                    if (bots[agt].item != null) {
                        tabooItems.add(bots[agt].item, 10);
                    }
                    bots[agt].item = null;
                    bots[agt].enemy = null;
                    bots[agt].itemsToRunAround = null;
                    navigation.stopNavigation();
                    break;
                case TARGET_REACHED:
                    //System.out.println("TARGET_REACHED");
                    bots[agt].item = null;
                    bots[agt].enemy = null;
                    bots[agt].itemsToRunAround = null;
                    navigation.stopNavigation();
                    break;
                default:
            }
        }
    };

    //重置
    protected void reset() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        //bot.getBotName().setInfo("RESET");
        bots[agt].reset();
        navigation.stopNavigation();
        return;
    }

    //主线程准备
    @Override
    public void prepareBot(UT2004Bot bot) {

        autoFixer = new UT2004PathAutoFixer(bot, navigation.getPathExecutor(), fwMap, aStar, navBuilder);

        for (int i = 0; i < agent_num; i++) {
            getWorldView().addEventListener(BotDamaged.class, botDamagedListener);
            getWorldView().addEventListener(PlayerDamaged.class, PlayerDamagedListener);
            getWorldView().addEventListener(PlayerKilled.class, PlayerKilledListener);
            getWorldView().addEventListener(BotKilled.class, BotKilledListener);
        }
        tabooItems = new TabooSet<Item>(bot);
        navigation.getState().addListener(FlagListener);

        weaponPrefs.addGeneralPref(UT2004ItemType.LIGHTNING_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.SHOCK_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.MINIGUN, false);
        weaponPrefs.addGeneralPref(UT2004ItemType.FLAK_CANNON, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ROCKET_LAUNCHER, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.LINK_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ASSAULT_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.BIO_RIFLE, true);
    }


    //产生
    @Override
    public void botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) {
        body.getCommunication().sendGlobalTextMessage("I am alive!");
    }


    //初始化
    @Override
    public Initialize getInitializeCommand() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        reset();
        return new Initialize().setName("Hunter_BASELINE-" + (++instanceCount)).setDesiredSkill(skill);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    //主程序
    private static int agent_num = 1;
    private static int skill = 3;
    private static boolean rw = false;

    public static void main(String[] args) throws PogamutException, Exception {
        agent = new AGENTS_ART[agent_num];
        bots = new Bots[agent_num];
        for (int i = 0; i < agent_num; i++) {
            agent[i] = new AGENTS_ART(i, numState, numAction, rw);
            bots[i] = new Bots(i, numState, numAction, rw);
        }
        new UT2004BotRunner(HunterBot_BASELINE.class, "Hunter").setMain(true).startAgents(agent_num);
    }


}
